package tld.course.homework1 
{
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	[SWF(width="600", height="600")]
	
	/**
	 * HOMEWORK:
	 * 1. Repetitium mater studiorum :) Change the background color of grid 
	 * cells to blue.
	 * 2. Fix the defect in the Grid class.
	 * 3. (Bonus) Add a class Computer to calculate computer moves. The computer
	 * must not loose. (Hint) You will also need to modify mouseDownHandler
	 * function so that it only plays for one side, while computer plays for 
	 * another side. Computer starts second.
	 * @author wvxvw
	 */
	public class TickTackToe extends Sprite
	{
		private static const BLACK:uint = 0xFF000000;
		private static const WHITE:uint = 0x00FFFFFF;
		private static const RED:uint = 0x80FF0000;
		
		private const _grid:Grid = new Grid();
		private const _eraser:Rectangle = new Rectangle();
		private const _line:Rectangle = new Rectangle();
		private const _canvas:Shape = new Shape();
		private const _position:Matrix = new Matrix();
		private const _destination:Point = new Point();
		private const _computer:Computer = new Computer();
		
		private var _image:BitmapData;
		private var _xImage:BitmapData;
		private var _oImage:BitmapData;
		private var _winnerImage:BitmapData;
		private var _gameOver:Boolean;
		
		public function TickTackToe() 
		{
			super();
			this.init();
		}
		
		private function init():void
		{
			this.drawGrid();
			super.stage.addEventListener(
				MouseEvent.MOUSE_DOWN, this.mouseDownHandler);
		}
		
		private function mouseDownHandler(event:MouseEvent):void
		{
			var positionX:uint = 
				Math.floor(super.mouseX / (super.stage.stageWidth / 3));
			var positionY:uint = 
				Math.floor(super.mouseY / (super.stage.stageHeight / 3));
			var cell:String;
			var winner:Vector.<uint>;
			
			if (this._gameOver)
			{
				this._gameOver = false;
				this._grid.clean();
				this.drawGrid();
			}
			cell = this._grid.get(positionX, positionY);
			if (!cell)
			{
				this._grid.set(positionX, positionY, "x");
				winner = this._grid.winner();
				if (!winner)
				{
					this._computer.makeTurn(this._grid);
					winner = this._grid.winner();
				}
				this.drawGrid();
				if (winner)
				{
					this.drawWin(winner);
					this._gameOver = true;
				}
			}
		}
		
		private function drawGrid():void
		{
			var mustRedraw:Boolean = 
				!this._image || this._image.width != this._eraser.height * 3;
			var canvas:Graphics = super.graphics;
			
			this._eraser.height = this._eraser.width = 
				Math.min(super.stage.stageHeight, super.stage.stageWidth) / 3;
			
			if (mustRedraw)
			{
				this._image = 
					new BitmapData(
						this._eraser.height * 3, 
						this._eraser.height * 3, 
						true, 
						WHITE);
				this.populateBitmapData();
			}
			
			for (var i:uint; i < 9; i++)
			{
				this.drawChar(
					this._grid.get(i / 3, i % 3), 
					uint(i / 3) * this._eraser.height, 
					uint(i % 3) * this._eraser.height);
			}
			this.drawCrossing();
			if (mustRedraw)
			{
				canvas.clear();
				canvas.beginBitmapFill(this._image);
				canvas.drawRect(0, 0, this._image.width, this._image.height);
			}
		}
		
		private function drawCrossing():void
		{
			this._line.width = this._image.width;
			this._line.height = 2;
			this._line.x = 0;
			this._line.y = this._eraser.height - 1;
			this._image.fillRect(this._line, BLACK);
			this._line.y += this._eraser.height - 1;
			this._image.fillRect(this._line, BLACK);
			this._line.y = 0;
			this._line.x = this._eraser.width - 1;
			this._line.height = this._image.height;
			this._line.width = 2;
			this._image.fillRect(this._line, BLACK);
			this._line.x += this._eraser.width - 1;
			this._image.fillRect(this._line, BLACK);
		}
		
		private function drawWin(line:Vector.<uint>):void
		{
			if (this._winnerImage) this._winnerImage.dispose();
			this._winnerImage = 
				new BitmapData(this._eraser.width, 
					this._eraser.height, true, RED);
			this._eraser.x = this._eraser.y = 0;
			for each (var i:uint in line)
			{
				this._destination.x = this._eraser.width * uint(i / 3);
				this._destination.y = this._eraser.height * uint(i % 3);
				this._image.merge(
					this._winnerImage, 
					this._eraser, 
					this._destination, 
					0xFF, 0xFF, 0xFF, 0x80);
			}
		}
		
		private function drawChar(character:String, 
			positionX:uint, positionY:uint):void
		{
			var characterPixels:BitmapData;
			
			this._eraser.x = positionX;
			this._eraser.y = positionY;
			this._image.fillRect(this._eraser, WHITE);
			if (character)
			{
				characterPixels = 
					(character == "x") ? this._xImage : this._oImage;
				this._position.tx = positionX;
				this._position.ty = positionY;
				this._image.draw(characterPixels, this._position);
			}
		}
		
		private function populateBitmapData():void
		{
			var canvas:Graphics = this._canvas.graphics;
			var xSide:uint;
			
			if (!this._xImage || this._xImage.width != this._eraser.width)
			{
				if (this._xImage) this._xImage.dispose();
				if (this._oImage) this._oImage.dispose();
				this._xImage = 
					new BitmapData(
						this._eraser.width, 
						this._eraser.width, 
						true, 
						WHITE);
				this._oImage = 
					new BitmapData(
						this._eraser.width, 
						this._eraser.width, 
						true, 
						WHITE);
				canvas.clear();
				canvas.lineStyle(5, BLACK);
				canvas.moveTo(5, 5);
				xSide = this._eraser.width - 5;
				canvas.lineTo(xSide, xSide);
				canvas.moveTo(xSide, 5);
				canvas.lineTo(5, xSide);
				this._xImage.draw(this._canvas);
				canvas.clear();
				canvas.lineStyle(5, BLACK);
				xSide = (this._eraser.width - 10) * 0.5;
				canvas.drawCircle(xSide + 5, xSide + 5, xSide);
				this._oImage.draw(this._canvas);
			}
		}
	}
}